#include <vector>
#include <string>
#include "Windows.h"
#include "../Host/Skugo.hpp"
#include "../Host/SkugoViewport.hpp"
#include "../Host/SkugoWeakReference.hpp"
#include "../Host/SkugoDebug.hpp"
#include "../Host/SkugoRtti.hpp"
#include "../Host/SkugoTypeBinding.hpp"
#include "../Host/SkugoMethodBinding.hpp"
#include "../Host/SkugoMemberBinding.hpp"

#include <time.h>

#include "Windows.h"

using namespace Skugo;

void TestBedRun();

void UnitTestWeakRef();
void UnitTestVariadicDelegate();

struct Animal
{
  SkugoDeclareType(Animal);

  void Speak();
};

int terrible;

SkugoDefineBaseType(Animal)
{
  //SkugoBindMethod(Speak)
  //  .Add(new Rpc());
}

int main(void)
{
  TestBedRun();

  UnitTestWeakRef();
  UnitTestVariadicDelegate();

  // Not necessary to call this (but recommended if we need to use inheritance tree for anything...)
  //InitializeAllTypes();

  const TypeInfo* t1 = SkugoTypeId(int);
  const TypeInfo* t2 = SkugoTypeId(short);
  const TypeInfo* t3 = SkugoTypeId(ushort);
  const TypeInfo* t4 = SkugoTypeId(uint64);

  const TypeInfo* t5 = SkugoTypeId(Animal);

  Framework* f = Framework::Instance();

  f->Start();

  clock_t start = clock();

  Viewport* viewport = NULL;


  for (;;)
  {
    clock_t current = clock();

    float seconds = (current - start) / (float)CLOCKS_PER_SEC;

    if (seconds > 5.0f && viewport == NULL)
    {
      viewport = f->CreateGlobal<Viewport>();
    }

    f->Update();

    Sleep(1);
  }

  f->Stop();
  
}


class Test1
{
public:
  SkugoDeclareWeak();
};

void UnitTestWeakRef()
{
  Test1* test1 = new Test1();

  Weak<Test1> w1(test1);

  SkugoErrorIf(w1.IsValid() == false, "Fail");

  Weak<Test1> w2(test1);

  SkugoErrorIf(w2.IsValid() == false, "Fail");

  Weak<Test1> w3(w2);

  SkugoErrorIf(w3.IsValid() == false, "Fail");

  w2 = w3;

  SkugoErrorIf(w1.IsValid() == false, "Fail");
  SkugoErrorIf(w2.IsValid() == false, "Fail");
  SkugoErrorIf(w3.IsValid() == false, "Fail");

  delete test1;

  SkugoErrorIf(w3.IsValid() == true, "Fail");
  SkugoErrorIf(w2.IsValid() == true, "Fail");
  SkugoErrorIf(w1.IsValid() == true, "Fail");
}

struct Test2
{
  SkugoDeclareType(Test2);

  void InstanceMethod(int& b)
  {
    SkugoErrorIf(mLives != 7, "Fail");
    SkugoErrorIf(b != 3, "Fail");
    b = mLives * 2 + b;
  }

  static void StaticMethod(int& c)
  {
    SkugoErrorIf(c != 15, "Fail");
    c += 12;
    c /= 3;
  }

  void InstanceMethod3ArgsAsPtr(int& b, float c, int* d)
  {
    SkugoErrorIf(mLives  != 2,    "Fail");
    SkugoErrorIf(b  != 6,    "Fail");
    SkugoErrorIf(c  != 4.5F, "Fail");
    SkugoErrorIf(*d != 2,    "Fail");
    b = (int)(c * b);
    b += *d + 1;
    b /= mLives;
    *d = -3;
  }

  float InstanceMethodRet(float b, int c)
  {
    SkugoErrorIf(mLives != 1,    "Fail");
    SkugoErrorIf(b != 4.5F, "Fail");
    SkugoErrorIf(c != 3,    "Fail");
    return b + c - mLives;
  }

  int Move(int b)
  {
    return ++mLives - b;
  }

  void Move(int x, int y)
  {
    mLives += x * (y + 3);
  }

  int mLives;
};

SkugoDefineBaseType(Test2)
{
  SkugoBindMethodAs(InstanceMethod, "ShurpaDerpa");

  SkugoBindMethod(InstanceMethodRet);

  SkugoBindMethod(InstanceMethod);

  SkugoBindMethodOverload(Move, int, int);

  SkugoBindMethodOverload(Move, void, int, int);

  SkugoBindMember(mLives);
}

void UnitTestVariadicDelegate()
{
  const TypeInfo* t = SkugoTypeId(Test2);
}
/*
void UnitTestVariadicDelegate()
{
  const TypeInfo* t = SkugoTypeId(Test2);

  // Instance method test
  {
    MethodInfo method = MethodBinding::FromInstanceMethod<(MethodBinding::CastInstanceMethod)&Test2::InstanceMethod>("Derp", &Test2::InstanceMethod);

    Test2 test2;
    test2.a = 7;

    Buffer parameters;
    Buffer returns;

    int b = 3;
    parameters.WriteReference<int&>(b, ReferenceForm::AsValue);
    method.Function(&test2, returns, parameters);

    int result = parameters.Read<int&>();
    SkugoErrorIf(result != 17, "Fail");
  }

  // Static method test
  {
    MethodInfo method = MethodBinding::FromStaticMethod<(MethodBinding::CastStaticMethod)&Test2::StaticMethod>("Twerp", &Test2::StaticMethod);

    Buffer parameters;
    Buffer returns;

    int b = 15;
    parameters.WriteReference<int&>(b, ReferenceForm::AsValue);
    method.Function(NULL, returns, parameters);

    int result = parameters.Read<int&>();
    SkugoErrorIf(result != 9, "Fail");
    SkugoErrorIf(b != 15,     "Fail");
  }

  // Instance method test (3 arguments)
  {
    MethodInfo method = MethodBinding::FromInstanceMethod<(MethodBinding::CastInstanceMethod)&Test2::InstanceMethod3ArgsAsPtr>("Herp", &Test2::InstanceMethod3ArgsAsPtr);

    Test2 test2;
    test2.a = 2;

    Buffer parameters;
    Buffer returns;

    int b = 6;
    int d = 2;
    parameters.WriteReference<int&>(b, ReferenceForm::AsPointer);
    parameters.Write<float>(4.5F);
    parameters.Write<int*>(&d);
    method.Function(&test2, returns, parameters);

    int result = parameters.Read<int&>();
    SkugoErrorIf(result != 15, "Fail");
    SkugoErrorIf(result != b,  "Fail");
    SkugoErrorIf(d != -3,      "Fail");
  }

  // Instance method test returns
  {
    MethodInfo method = MethodBinding::FromInstanceMethod<(MethodBinding::CastInstanceMethod)&Test2::InstanceMethodRet>("Cherp", &Test2::InstanceMethodRet);

    Test2 test2;
    test2.a = 1;

    Buffer parameters;
    Buffer returns;

    float b = 4.5f;
    int c = 3;
    parameters.Write<float>(b);
    parameters.Write<int>(c);
    method.Function(&test2, returns, parameters);

    float result = returns.Read<float>();
    SkugoErrorIf(b != 4.5f,       "Fail");
    SkugoErrorIf(c != 3,          "Fail");
    SkugoErrorIf(result != 6.5f,  "Fail");
  }
}
*/

